/*
//
//              INTEL CORPORATION PROPRIETARY INFORMATION
//  This software is supplied under the terms of a license  agreement or
//  nondisclosure agreement with Intel Corporation and may not be copied
//  or disclosed except in  accordance  with the terms of that agreement.
//    Copyright (c) 2007 Intel Corporation. All Rights Reserved.
//
//
*/

#include "umc_defs.h"
#if defined(UMC_ENABLE_AVS_SPLITTER) || \
    defined(UMC_ENABLE_AVS_VIDEO_DECODER) || \
    defined(UMC_ENABLE_AVS_VIDEO_ENCODER)

#include <trace_log.h>
#include "umc_avs_sequence_header.h"
#include "umc_avs_dec_byte_stream.h"

namespace UMC
{

static
double AVSFrameRates[16] =
{
    15.0,
    23.976,
    24.0,
    25.0,
    29.97,
    30.0,
    50.0,
    59.94,
    60.0,

    16.0,
    17.0,
    18.0,
    19.0,
    20.0,
    21.0,
    22.0
};

Status DecodeAVSSequenceHeader(AVS_SEQUENCE_HEADER *pSeqHeader,
                               const Ipp8u *pbSrc,
                               size_t srcSize)
{
    AVS_SEQUENCE_HEADER seqHeader;
    AVSByteStream byteStream;
    Status umcRes;
    Ipp32s iTemp;

    // the sequence header occupies exactly 18 bytes.
    if (18 > srcSize)
        return UMC_ERR_INVALID_STREAM;

    // initialize the byte stream
    umcRes = byteStream.Init();
    if (UMC_OK != umcRes)
        return umcRes;
    umcRes = byteStream.SetSource(pbSrc, srcSize);
    if (UMC_OK != umcRes)
        return umcRes;

    WRITE_TRACE_LOG(_sqh_dec, "sequence header", 0);

    // skipping start code
    byteStream.SkipBits(24);

    // read information
    seqHeader.video_sequence_start_code = byteStream.GetBits(8);
    WRITE_TRACE_LOG(_sqh_dec, "video_sequence_start_code", seqHeader.video_sequence_start_code);
    seqHeader.profile_id = byteStream.GetBits(8);
    WRITE_TRACE_LOG(_sqh_dec, "profile_id", seqHeader.profile_id);
    seqHeader.level_id = byteStream.GetBits(8);
    WRITE_TRACE_LOG(_sqh_dec, "level_id", seqHeader.level_id);
    seqHeader.progressive_sequence = byteStream.GetBit();
    WRITE_TRACE_LOG(_sqh_dec, "progressive_sequence", seqHeader.progressive_sequence);
    seqHeader.horizontal_size = byteStream.GetBits(14);
    WRITE_TRACE_LOG(_sqh_dec, "horizontal_size", seqHeader.horizontal_size);
    seqHeader.vertical_size = byteStream.GetBits(14);
    WRITE_TRACE_LOG(_sqh_dec, "vertical_size", seqHeader.vertical_size);
    seqHeader.chroma_format = byteStream.GetBits(2);
    WRITE_TRACE_LOG(_sqh_dec, "chroma_format", seqHeader.chroma_format);
    seqHeader.sample_precission = byteStream.GetBits(3);
    WRITE_TRACE_LOG(_sqh_dec, "sample_precission", seqHeader.sample_precission);
    seqHeader.aspect_ratio = byteStream.GetBits(4);
    WRITE_TRACE_LOG(_sqh_dec, "aspect_ratio", seqHeader.aspect_ratio);
    iTemp = byteStream.GetBits(4);
    WRITE_TRACE_LOG(_sqh_dec, "frame_rate code", iTemp);
    seqHeader.frame_rate = AVSFrameRates[iTemp];
    seqHeader.bit_rate_lower = byteStream.GetBits(18);
    WRITE_TRACE_LOG(_sqh_dec, "bit_rate_lower", seqHeader.bit_rate_lower);
    byteStream.SkipBits(1);
    seqHeader.bit_rate_upper = byteStream.GetBits(12);
    WRITE_TRACE_LOG(_sqh_dec, "bit_rate_upper", seqHeader.bit_rate_upper);
    seqHeader.low_delay = byteStream.GetBit();
    WRITE_TRACE_LOG(_sqh_dec, "low_delay", seqHeader.low_delay);
    byteStream.SkipBits(1);
    seqHeader.bbv_buffer_size = byteStream.GetBits(18);
    WRITE_TRACE_LOG(_sqh_dec, "bbv_buffer_size", seqHeader.bbv_buffer_size);
    byteStream.SkipBits(3);

    // check error(s)
    if (UMC_OK != byteStream.Close())
        return UMC_ERR_FAILED;
    if (0x0b0 != seqHeader.video_sequence_start_code)
        return UMC_ERR_INVALID_STREAM;
    if (0 == seqHeader.profile_id)
        return UMC_ERR_INVALID_STREAM;
    if (0 == seqHeader.level_id)
        return UMC_ERR_INVALID_STREAM;
    if (0 == seqHeader.sample_precission)
        return UMC_ERR_INVALID_STREAM;
    if (0 == seqHeader.aspect_ratio)
        return UMC_ERR_INVALID_STREAM;

    // copy decoded header
    *pSeqHeader = seqHeader;

    return UMC_OK;

} // Status DecodeAVSSequenceHeader(AVS_SEQUENCE_HEADER *pSeqHeader,

Status EncodeAVSSequenceHeader(Ipp8u **pbDst,
                               size_t dstSize,
                               const AVS_SEQUENCE_HEADER *pSeqHeader)
{
    AVSByteStream byteStream;
    Status umcRes;
    Ipp32u iTemp, i;

    // the sequence header occupies exactly 18 bytes.
    if (18 > dstSize)
        return UMC_ERR_NOT_ENOUGH_BUFFER;

    // initialize the byte stream
    umcRes = byteStream.Init();
    if (UMC_OK != umcRes)
        return umcRes;
    umcRes = byteStream.SetDestination(*pbDst, dstSize);
    if (UMC_OK != umcRes)
        return umcRes;

    WRITE_TRACE_LOG(_sqh_enc, "sequence header", 0);

    // skipping start code
    byteStream.PutBits(1, 24);

    // save information
    byteStream.PutBits(pSeqHeader->video_sequence_start_code, 8);
    WRITE_TRACE_LOG(_sqh_enc, "video_sequence_start_code", pSeqHeader->video_sequence_start_code);
    byteStream.PutBits(pSeqHeader->profile_id, 8);
    WRITE_TRACE_LOG(_sqh_enc, "profile_id", pSeqHeader->profile_id);
    byteStream.PutBits(pSeqHeader->level_id, 8);
    WRITE_TRACE_LOG(_sqh_enc, "level_id", pSeqHeader->level_id);
    byteStream.PutBit(pSeqHeader->progressive_sequence);
    WRITE_TRACE_LOG(_sqh_enc, "progressive_sequence", pSeqHeader->progressive_sequence);
    byteStream.PutBits(pSeqHeader->horizontal_size, 14);
    WRITE_TRACE_LOG(_sqh_enc, "horizontal_size", pSeqHeader->horizontal_size);
    byteStream.PutBits(pSeqHeader->vertical_size, 14);
    WRITE_TRACE_LOG(_sqh_enc, "vertical_size", pSeqHeader->vertical_size);
    byteStream.PutBits(pSeqHeader->chroma_format, 2);
    WRITE_TRACE_LOG(_sqh_enc, "chroma_format", pSeqHeader->chroma_format);
    byteStream.PutBits(pSeqHeader->sample_precission, 3);
    WRITE_TRACE_LOG(_sqh_enc, "sample_precission", pSeqHeader->sample_precission);
    byteStream.PutBits(pSeqHeader->aspect_ratio, 4);
    WRITE_TRACE_LOG(_sqh_enc, "aspect_ratio", pSeqHeader->aspect_ratio);
    iTemp = 0;
    for (i = 0; i < (sizeof(AVSFrameRates) / sizeof(AVSFrameRates[0])); i += 1)
    {
        if (AVSFrameRates[i] == pSeqHeader->frame_rate)
        {
            iTemp = i;
        }
    }

    byteStream.PutBits(iTemp, 4);
    WRITE_TRACE_LOG(_sqh_enc, "frame_rate code", iTemp);
    byteStream.PutBits(pSeqHeader->bit_rate_lower, 18);
    WRITE_TRACE_LOG(_sqh_enc, "bit_rate_lower", pSeqHeader->bit_rate_lower);
    byteStream.PutBit(1);
    byteStream.PutBits(pSeqHeader->bit_rate_upper, 12);
    WRITE_TRACE_LOG(_sqh_enc, "bit_rate_upper", pSeqHeader->bit_rate_upper);
    byteStream.PutBit(pSeqHeader->low_delay);
    WRITE_TRACE_LOG(_sqh_enc, "low_delay", pSeqHeader->low_delay);
    byteStream.PutBit(1);
    byteStream.PutBits(pSeqHeader->bbv_buffer_size, 18);
    WRITE_TRACE_LOG(_sqh_enc, "bbv_buffer_size", pSeqHeader->bbv_buffer_size);
    byteStream.PutBits(0, 3);
    byteStream.Flush();

    // check error(s)
    if (UMC_OK != byteStream.Close())
        return UMC_ERR_FAILED;
    if (0x0b0 != pSeqHeader->video_sequence_start_code)
        return UMC_ERR_INVALID_STREAM;
    if (0 == pSeqHeader->profile_id)
        return UMC_ERR_INVALID_STREAM;
    if (0 == pSeqHeader->level_id)
        return UMC_ERR_INVALID_STREAM;
    if (0 == pSeqHeader->sample_precission)
        return UMC_ERR_INVALID_STREAM;
    if (0 == pSeqHeader->aspect_ratio)
        return UMC_ERR_INVALID_STREAM;

    // update destination pointer
    *pbDst = byteStream.GetPointer();

    return UMC_OK;

} // Status EncodeAVSSequenceHeader(Ipp8u *pbDst,

} // namespace UMC

#endif // #if defined(UMC_ENABLE_AVS_SPLITTER) || defined(UMC_ENABLE_AVS_VIDEO_DECODER)
